home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / managers / mc-3.2 / mc-3 / mc-3.2.1 / src / cons.saver.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  9.4 KB  |  392 lines

  1. #if defined(linux) || defined(__linux__)
  2.  
  3. /* General purpose Linux console screen save/restore server
  4.    Copyright (C) 1994 Janne Kukonlehto <jtklehto@stekt.oulu.fi>
  5.    Original idea from Unix Interactive Tools version 3.2b (tty.c)
  6.    This code requires root privileges.
  7.    You may want to make the cons.saver setuid root.
  8.    The code should be safe even if it is setuid but who knows?
  9.    
  10.    This program is free software; you can redistribute it and/or modify
  11.    it under the terms of the GNU General Public License as published by
  12.    the Free Software Foundation; either version 2 of the License, or
  13.    (at your option) any later version.
  14.    
  15.    This program is distributed in the hope that it will be useful,
  16.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.    GNU General Public License for more details.
  19.  
  20.    You should have received a copy of the GNU General Public License
  21.    along with this program; if not, write to the Free Software
  22.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  23.  
  24. #include <config.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <sys/ioctl.h>
  28. #include <fcntl.h>
  29. #include <unistd.h>
  30. #include <termios.h>
  31. #include <stdlib.h>
  32. #include <stdio.h>
  33. #include <ctype.h>    /* For isdigit() */
  34. typedef struct WINDOW WINDOW;
  35. #include "cons.saver.h"
  36.  
  37. #define cmd_input 0
  38. #define cmd_output 1
  39.  
  40. /* Meaning of console_flag:
  41.    -1 == to be detected,
  42.    0  == not a console
  43.    1  == is a console, Linux < 1.1.67 (black & white)
  44.    2  == is a console, Linux >= 1.1.67 (color)
  45.    3  == is a console, Linux >= 1.1.92 (color, use /dev/vcsa$num
  46.    */
  47. static signed char console_flag = -1;
  48. /*
  49.    Meaning of console_fd:
  50.    -1  == not opened,
  51.    >=0 == opened
  52.    */
  53. static int console_fd = -1;
  54. static char *tty_name;
  55. static int len;
  56. static char *buffer = NULL;
  57. static int buffer_size = 0;
  58. static int columns, rows;
  59. static char vcs_name [40];
  60. static int vcs_fd;
  61.  
  62. static void dwrite (int fd, char *buffer)
  63. {
  64.     write (fd, buffer, strlen (buffer));
  65. }
  66.  
  67. static void tty_getsize ()
  68. {
  69.     struct winsize winsz;
  70.  
  71.     winsz.ws_col = winsz.ws_row = 0;
  72.     ioctl (console_fd, TIOCGWINSZ, &winsz);
  73.     if (winsz.ws_col && winsz.ws_row){
  74.     columns = winsz.ws_col;
  75.     rows    = winsz.ws_row;
  76.     } else {
  77.     /* Never happens (I think) */
  78.     dwrite (2, "TIOCGWINSZ failed\n");
  79.     columns = 80;
  80.     rows = 25;
  81.     console_flag = 0;
  82.     }
  83. }
  84.  
  85. inline void tty_cursormove(int y, int x)
  86. {
  87.     char buffer [20];
  88.  
  89.     /* Standard ANSI escape sequence for cursor positioning */
  90.     sprintf (buffer,"\33[%d;%dH", y + 1, x + 1);
  91.     dwrite (console_fd, buffer);
  92. }
  93.  
  94. int check_file (char *filename, int check_console, char **msg)
  95. {
  96.     int fd;
  97.     struct stat stat_buf;
  98.  
  99.     /* Avoiding race conditions: use of fstat makes sure that
  100.        both 'open' and 'stat' operate on the same file */
  101.  
  102.     *msg = 0;
  103.     
  104.     fd = open (filename, O_RDWR);
  105.     if (fd == -1)
  106.     return -1;
  107.     
  108.     if (fstat (fd, &stat_buf) == -1)
  109.     return -1;
  110.  
  111.     /* Must be character device */
  112.     if (!S_ISCHR (stat_buf.st_mode)){
  113.     *msg = "Not a character device";
  114.     return -1;
  115.     }
  116.  
  117. #ifdef DEBUG
  118.     fprintf (stderr, "Device: %x\n", stat_buf.st_rdev);
  119. #endif
  120.     if (check_console){
  121.     /* Second time: must be console */
  122.     if ((stat_buf.st_rdev & 0xff00) != 0x0400){
  123.         *msg = "Not a console";
  124.         return -1;
  125.     }
  126.     
  127.     if ((stat_buf.st_rdev & 0x00ff) > 9){
  128.         *msg = "Minor device number too big";
  129.         return -1;
  130.     }
  131.     
  132.     /* Must be owned by the user */
  133.     if (stat_buf.st_uid != getuid ()){
  134.         *msg = "Not a owner";
  135.         return -1;
  136.     }
  137.     }
  138.     
  139.     /* Everything seems to be okay */
  140.     return fd;
  141. }
  142.  
  143. /* Detect console */
  144. /* Because the name of the tty is supplied by the user and this
  145.    can be a setuid program a lot of checks has to done to avoid
  146.    creating a security hole */
  147. char *detect_console (void)
  148. {
  149.     char *msg;
  150.     int  xlen;
  151.     
  152.     /* Root privileges required */
  153.     if (geteuid () != 0)
  154.     return "No privileges";
  155.  
  156.     /* Must be console */
  157.     /* Handle the case for /dev/tty?? */
  158.     if (tty_name[len-5] == 't')
  159.     xlen = len + 1;
  160.     else
  161.     xlen = len;
  162.  
  163.     /* General: /dev/ttyn */
  164.     if (tty_name[xlen - 5] != '/' ||
  165.     tty_name[xlen - 4] != 't' ||
  166.     tty_name[xlen - 3] != 't' ||
  167.     tty_name[xlen - 2] != 'y' ||
  168.     !isdigit(tty_name[len - 1]))
  169.     return "Doesn't look like console";
  170.  
  171.     sprintf (vcs_name, "/dev/vcsa%c", tty_name [len-1]);
  172.     vcs_fd = check_file (vcs_name, 0, &msg);
  173.     console_fd = check_file (tty_name, 1, &msg);
  174.  
  175. #ifdef DEBUG
  176.     fprintf (stderr, "vcs_fd = %d console_fd = %d\n", vcs_fd, console_fd);
  177. #endif
  178.     
  179.     if (vcs_fd != -1){
  180.     console_flag = 3;
  181.     }
  182.  
  183.     if (console_fd == -1)
  184.     return msg;
  185.  
  186.     return NULL;
  187. }
  188.  
  189. void save_console (void)
  190. {
  191.     int i;
  192.  
  193.     if (!console_flag)
  194.     return;
  195.     buffer [1] = tty_name [len-1] - '0';
  196.     if (console_flag >= 2){
  197.     /* Linux >= 1.1.67 */
  198.     /* Get screen contents and cursor position */
  199.     buffer [0] = 8;
  200.     if (console_flag == 2){
  201.         if ((i = ioctl (console_fd, TIOCLINUX, buffer)) == -1){
  202.         /* Oops, this is not Linux 1.1.67 */
  203.         console_flag = 1;
  204.         }
  205.     } else {
  206.         lseek (vcs_fd, 0, 0);
  207.         read (vcs_fd, buffer, buffer_size);
  208.     }
  209.     }
  210.     if (console_flag == 1){
  211.     int index, x, y;
  212.  
  213.     /* Linux < 1.1.67 */
  214.     /* Get screen contents */
  215.     buffer [0] = 0;
  216.     if (ioctl(console_fd, TIOCLINUX, buffer) == -1){
  217.         buffer[0] = buffer[1] = 0;
  218.  
  219.         /* Linux bug: bad ioctl on console 8 */
  220.         if (ioctl(console_fd, TIOCLINUX, buffer) == -1){
  221.         /* Oops, this is not a console after all */
  222.         console_flag = 0;
  223.         return;
  224.         }
  225.     }
  226.     /* Select the beginning of the bottommost empty line
  227.        to be the cursor position */
  228.     index = 2 + rows * columns;
  229.     for (y = rows - 1; y >= 0; y--)
  230.         for (x = columns - 1; x >= 0; x--)
  231.         if (buffer[--index] != ' ')
  232.             goto non_space_found;
  233.     non_space_found:
  234.     buffer[0] = y + 1;
  235.     buffer[1] = 0;
  236.     /*tty_cursormove(y + 1, 0);*/
  237.     }
  238. }
  239.  
  240. void restore_console (void)
  241. {
  242.     if (!console_flag)
  243.     return;
  244.     if (console_flag == 2){
  245.     /* Linux >= 1.1.67 */
  246.     /* Restore screen contents and cursor position */
  247.     buffer [0] = 9;
  248.     buffer [1] = tty_name [len-1] - '0';
  249.     ioctl (console_fd, TIOCLINUX, buffer);
  250.     }
  251.     if (console_flag == 3){
  252.     lseek (vcs_fd, 0, 0);
  253.     write (vcs_fd, buffer, buffer_size);
  254.     }
  255.     if (console_flag == 1){
  256.     /* Clear screen */
  257.     write(console_fd, "\033[H\033[2J", 7);
  258.     /* Output saved screen contents */
  259.     write(console_fd, buffer + 2, rows * columns);
  260.     /* Move the cursor to the previously selected position */
  261.     tty_cursormove(buffer[0], buffer[1]);
  262.     }
  263. }
  264.  
  265. void send_contents ()
  266. {
  267.     int begin_line=0, end_line=0;
  268.     int index, x, y;
  269.     int lastline;
  270.     int message;
  271.     int bytes_per_char;
  272.     
  273.     bytes_per_char = console_flag == 1 ? 1 : 2;
  274.     
  275.     /* Calculate the number of used lines */
  276.     if (console_flag == 2 || console_flag == 1 || console_flag == 3){
  277.     index = (2 + rows * columns) * bytes_per_char;
  278.     for (y = rows - 1; y >= 0; y--)
  279.         for (x = columns - 1; x >= 0; x--){
  280.         index -= bytes_per_char;
  281.         if (buffer[index] != ' ')
  282.             goto non_space_found;
  283.         }
  284.     non_space_found:
  285.     lastline = y + 1;
  286.     } else
  287.     return;
  288.  
  289.     /* Inform the invoker that we can handle this command */
  290.     message = CONSOLE_CONTENTS;
  291.     write (cmd_output, &message, 1);
  292.  
  293.     /* Read the range of lines wanted */
  294.     read (cmd_input, &begin_line, 1);
  295.     read (cmd_input, &end_line, 1);
  296.     if (begin_line > lastline)
  297.     begin_line = lastline;
  298.     if (end_line > lastline)
  299.     end_line = lastline;
  300.  
  301.     /* Tell the invoker how many bytes it will be */
  302.     message = (end_line - begin_line) * columns;
  303.     write (cmd_output, &message, 2);
  304.  
  305.     /* Send the contents */
  306.     for (index = (2 + begin_line * columns) * bytes_per_char;
  307.      index < (2 + end_line * columns) * bytes_per_char;
  308.      index += bytes_per_char)
  309.     write (cmd_output, buffer + index, 1);
  310.  
  311.     /* All done */
  312. }
  313.  
  314. int main (int argc, char **argv)
  315. {
  316.     char *error;
  317.     int  action = 0;
  318.  
  319.     if (argc != 2){
  320.     /* Wrong number of arguments */
  321.  
  322.     dwrite (2, "Usage: cons.saver <ttyname>\n");
  323.     console_flag = 0;
  324.     write (cmd_output, &console_flag, 1);
  325.     return 3;
  326.     }
  327.  
  328.     /* Lose the control terminal */
  329.     setsid ();
  330.     
  331.     /* Check that the argument is a legal console */
  332.     tty_name = argv [1];
  333.     len = strlen(tty_name);
  334.     error = detect_console ();
  335.  
  336.     if (error){
  337.     /* Not a console -> no need for privileges */
  338.     setuid (getuid ());
  339.     dwrite (2, error);
  340.     console_flag = 0;
  341.     if (console_fd >= 0)
  342.         close (console_fd);
  343.     } else {
  344.     /* Console was detected */
  345.     if (console_flag != 3)
  346.         console_flag = 2; /* Default to Linux >= 1.1.67 */
  347.     /* Allocate buffer for screen image */
  348.     tty_getsize ();
  349.     buffer_size = 4 + 2 * columns * rows;
  350.     buffer = (char*) malloc (buffer_size);
  351.     }
  352.  
  353.     /* If using /dev/vcs*, we don't need anymore the console fd */
  354.     if (console_flag == 3)
  355.     close (console_fd);
  356.     
  357.     /* Inform the invoker about the result of the tests */
  358.     write (cmd_output, &console_flag, 1);
  359.  
  360.     /* Read commands from the invoker */
  361.     while (console_flag && read (cmd_input, &action, 1)){
  362.     /* Handle command */
  363.     switch (action){
  364.     case CONSOLE_DONE:
  365.         console_flag = 0;
  366.         continue; /* Break while loop instead of switch clause */
  367.     case CONSOLE_SAVE:
  368.         save_console ();
  369.         break;
  370.     case CONSOLE_RESTORE:
  371.         restore_console ();
  372.         break;
  373.     case CONSOLE_CONTENTS:
  374.         send_contents ();
  375.         break;
  376.     } /* switch (action) */
  377.         
  378.     /* Inform the invoker that command is handled */
  379.     write (cmd_output, &console_flag, 1);
  380.     } /* while (read ...) */
  381.  
  382.     if (buffer)
  383.     free (buffer);
  384.     return 0;   
  385. }
  386.  
  387. #else
  388.  
  389. #error The Linux console screen saver works only on Linux.
  390.  
  391. #endif /* #ifdef linux */
  392.